#include "proxy/ping/pingclient.h"
#include "proxy/cfg/localconfiginstance.h"

namespace proxy{

  PingClient::PingClient(boost::asio::io_service& io_service, int timeout)
    :io_service_(io_service),
    timeout_(timeout),
    ping_times_(0){

  }

  PingClient::~PingClient(){
    LOG(WARNING) << "Delte PingClient";
  }

  void PingClient::Start(){

    ConfigInstance *config = ConfigInstance::GetConfigInstance();

    connect_ = vzconn::TcpClient::ConnectServer(
      io_service_, 
      "127.0.0.1", 
      10003,
      boost::bind(&PingClient::OnServerConnect, shared_from_this(), _1, _2));
  }

  void PingClient::StartPingMessage(){
    timer_.reset(new boost::asio::deadline_timer(
      io_service_, boost::posix_time::seconds(timeout_)));
    timer_->async_wait(
      boost::bind(&PingClient::HandleTimeout, shared_from_this(),
      boost::asio::placeholders::error));
  }

  void PingClient::OnServerConnect(vzconn::TcpConnect::Ptr connect,
    const boost::system::error_code& err){
    BOOST_ASSERT(connect == connect_);
    if (err){
      LOG(ERROR) << err.message();
      SignalPingError(shared_from_this(), err);
      return;
    }

    connect_->SignalConnectError.connect(
      boost::bind(&PingClient::OnConnectError, shared_from_this(), _1, _2));
    connect_->SignalConnectWrite.connect(
      boost::bind(&PingClient::OnConnectWrite, shared_from_this(), _1));
    connect_->SignalConnectRead.connect(
      boost::bind(&PingClient::OnConnectRead, shared_from_this(), _1, _2, _3, _4));


    StartRegisterServer();
  }

  void PingClient::StartRegisterServer(){
    ConfigInstance *config = ConfigInstance::GetConfigInstance();

    boost::shared_ptr<vzconn::ByteBuffer> bytebuffer(new vzconn::ByteBuffer());
    // nodetype
    //bytebuffer->WriteUInt8(config->node_type());
    //// Proxy Comment
    //bytebuffer->WriteUInt8(config->fake_commit());
    //// Proxy port
    //bytebuffer->WriteUInt16(config->bind_port());
    //// Domain size
    //bytebuffer->WriteUInt8(config->domain().size());
    //// domain
    //bytebuffer->WriteBytes(config->domain().c_str(), config->domain().size());

    if (connect_){
      connect_->AsyncWrite(bytebuffer, FLAG_REGISTER_PING);
    }
    else{
      SignalPingError(shared_from_this(), boost::asio::error::invalid_argument);
    }
  }

  bool PingClient::StartGetOnlineProxyServer(const char* user_name, int size, 
    SignalOnlineServerListCallback callback){
    SignalOnlineServerList = callback;
    if (connect_){
      connect_->AsyncWrite(user_name, size, FLAG_PROXY_ONLINE_SERVER_LIST);
      return true;
    }
    return false;
  }

  void PingClient::HandleTimeout(const boost::system::error_code& err){
    if (!connect_){
      LOG(ERROR) << "connect_ delete";
      return;
    }
    if (err){
      LOG(ERROR) << err.message();
      SignalPingError(shared_from_this(), err);
      return;
    }
    if (ping_times_ <= PING_TIMEOUT_TIMES){
      ping_times_++;
      int64 current_time = 0;
      time(&current_time);
      connect_->AsyncWrite((const char *)&current_time, 8, FLAG_PROXY_SERVER_PING_MESSAGE);

      timer_->expires_from_now(boost::posix_time::seconds(timeout_));
      timer_->async_wait(
        boost::bind(&PingClient::HandleTimeout, shared_from_this(),
        boost::asio::placeholders::error));
    }
    else{
      LOG(ERROR) << "Ping time out";
      SignalPingError(shared_from_this(), err);
    }
  }


  void PingClient::OnConnectError(vzconn::TcpConnect::Ptr connect,
    const boost::system::error_code& err){
    connect_.reset();
    SignalPingError(shared_from_this(), err);
  }

  void PingClient::OnConnectWrite(vzconn::TcpConnect::Ptr connect){
    // Donothing
  }

  void PingClient::OnConnectRead(vzconn::TcpConnect::Ptr connect,
    const char* buffer, int size, int flag){
    if (flag == FLAG_PROXY_SERVER_PING_MESSAGE){
      LOG(INFO) << "FLAG_PROXY_SERVER_PING_MESSAGE ";
      connect_->AsyncWrite(buffer, size, FLAG_PROXY_SERVER_PING_REPLY);
    }
    else if (flag == FLAG_PROXY_SERVER_PING_REPLY){
      ping_times_ = 0;
      uint64 ping_time = *(uint64*)(buffer);
      int64 current_time = 0;
      time(&current_time);
      LOG(INFO) << "PING " << current_time - ping_time;
    }
    else if (flag == FLAG_PROXY_ONLINE_SERVER_LIST){
      SignalOnlineServerList(shared_from_this(), buffer, size);
    }
    else if (flag == FLAG_REGISTER_PING_SUCCEED){
      LOG(INFO) << "Register succeed";
      SignalPingSucceed(shared_from_this());
      StartPingMessage();
    }
    else if (flag == FLAG_REGISTER_PING_FAILURE){
      LOG(INFO) << "Register error";
      SignalPingError(shared_from_this(), boost::asio::error::invalid_argument);
    }
    else{
      LOG(ERROR) << "Nothing to do this";
    }
  }
}